package com.pink.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.pink.entity.config.AppConfig;
import com.pink.entity.config.Constants;
import com.pink.entity.dto.UserDto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class JwtUtils {

    // 注入配置类
    @Resource
    AppConfig appConfig;

    // 注入Redis模板
    @Resource
    StringRedisTemplate template;


    // 解析JWT
    public DecodedJWT resolveJwt(String headerToken) {
        // 将headerToken转换为token
        String token = this.convertToken(headerToken);
        // 如果token为空，则返回null
        if (token == null) return null;
        // 使用HMAC256算法和配置的key生成算法
        Algorithm algorithm = Algorithm.HMAC256(appConfig.getKey());
        // 使用算法生成JWT验证器
        JWTVerifier jwtverifier = JWT.require(algorithm).build();
        try {
            // 验证token
            DecodedJWT verifier = jwtverifier.verify(token);
            // 如果token无效，则返回null
            String userId = verifier.getId();
            if (this.isInvalidToken(userId))
                return null;
            // 获取token的过期时间
            Date expiresAt = verifier.getExpiresAt();
            // 如果当前时间在过期时间之后，则返回null
            return new Date().after(expiresAt) ? null : verifier;
        }catch (JWTVerificationException e){
            // 如果验证失败，则返回null
            return null;
        }
    }

    // 使JWT失效
    public boolean invalidateJwt(String headerToken) {
        // 将headerToken转换为token
        String token = this.convertToken(headerToken);
        // 如果token为空，则返回false
        if (token == null) return false;
        // 使用HMAC256算法和配置的key生成算法
        Algorithm algorithm = Algorithm.HMAC256(appConfig.getKey());
        // 使用算法生成JWT验证器
        JWTVerifier jwtverifier = JWT.require(algorithm).build();
        try{
            // 验证token
            DecodedJWT jwt = jwtverifier.verify(token);
            // 获取token的id
            String id = jwt.getId();
            // 删除token
            return deleteToken(id,jwt.getExpiresAt());
        }catch (JWTVerificationException e){
            // 如果验证失败，则返回false
            return false;
        }
    }

    public boolean deleteToken(String uuid,Date time) {
        // 如果token无效，则返回false
        if (this.isInvalidToken(uuid)) {
            return false;
        };
        // 更新token
        return this.updateToken(uuid,time);
    }

    // 更新token
    private  boolean updateToken(String uuid,Date time) {
        // 如果token无效，则返回false
        if (this.isInvalidToken(uuid)) return false;
        // 获取当前时间
        Date now = new Date();
        // 计算过期时间
        long expire = Math.abs(now.getTime() - time.getTime());
        if(expire < 1000){
            template.delete(Constants.JWT_BLACK_LIST + uuid);
            return false;
        }
        // 将token存入Redis，并设置过期时间
        template.opsForValue().set(Constants.JWT_BLACK_LIST + uuid, "1", expire, TimeUnit.MILLISECONDS);
        // 返回true
        return true;
    }

    // 判断token是否无效
    private boolean isInvalidToken(String uuid) {
        // 如果Redis中存在该token，则返回true
        return Boolean.TRUE.equals(template.hasKey(Constants.JWT_BLACK_LIST + uuid));
    }

    // 创建JWT
    public String createJwt(UserDetails details, UserDto user){
        // 使用HMAC256算法和配置的key生成算法
        Algorithm algorithm = Algorithm.HMAC256(appConfig.getKey());
        // 获取过期时间
        Date expire = this.expirTime();
        // 生成一个UUID
        String uuid = UUID.randomUUID().toString();
        // 创建JWT
        String jwt = JWT.create()
                // 设置JWT的id
                .withJWTId(uuid)
                // 设置JWT的userid
                .withClaim("userId",user.getUserId())
                // 设置JWT的name
                .withClaim("username",user.getUsername())
                // 设置JWT的authorities
                .withClaim("authorities","user")
                // 设置JWT的过期时间
                .withExpiresAt(expire)
                // 设置JWT的签发时间
                .withIssuedAt(new Date())
                // 使用算法签名
                .sign(algorithm);
        // token存入Redis，并设置过期时间
        template.opsForValue().set(Constants.JWT_BLACK_LIST + uuid,jwt,appConfig.getExpire() * 24L,TimeUnit.MICROSECONDS);
        // 创建JWT
        return jwt;
    }
    // 获取过期时间
    public Date expirTime(){
        // 获取当前时间
        Calendar calendar = Calendar.getInstance();
        // 将当前时间加上配置的过期时间
        calendar.add(Calendar.HOUR,appConfig.getExpire() * 24);
        // 返回过期时间
        return calendar.getTime();
    }

    // 将JWT转换为UserDetails
    public UserDetails toUser(DecodedJWT jwt) {
        // 获取JWT的claims
        Map<String, Claim> claims = jwt.getClaims();
        // 返回UserDetails
        return User
                // 设置用户名
                .withUsername(claims.get("username").asString())
                // 设置密码
                .password("******")
                // 设置权限
                .authorities(claims.get("authorities").asString())
                // 构建UserDetails
                .build();
    }

    // 将JWT转换为id
    public Integer toId(DecodedJWT jwt) {
        // 获取JWT的claims
        Map<String, Claim>  claims = jwt.getClaims();
        // 返回id
        return claims.get("userId").asInt();
    }

    // 将headerToken转换为token
    private String convertToken(String headerToken) {
        // 如果headerToken为空或者不以"Bearer "开头，则返回null
        if (headerToken == null || !headerToken.startsWith("Bearer ")) {
            return null;
        }
        // 返回token
        return headerToken.substring(7);
    }
}